import { Group } from "./Group";
import { Object3D } from "../entity/Object3D";
import { Camera } from "../component/Camera";
import { Register } from "../register/Register";
import {
  ComponentType,
  PunctualLight,
  MeshRender,
  Background,
  HemisLight,
  AmbientLight,
  Orbit,
  EnviromentLight,
  PostProcessing,
  Pick,
} from "../component/index";
import { Event, EventType } from "../event/index";
import { SkyboxGeometry } from "../geom/SkyboxGeometry";
import { SkyboxMaterial } from "../material/SkyboxMaterial";
import { Mesh } from "../component/Mesh";
import { Popup } from "../popup/Popup";
import { must } from "../core/logic";
import { Pass } from "../component/post/Pass";
import { Plot } from "../component/controller/Plot";
import { Transform } from "../component/controller/Transform";
import { any } from "../core/logic";
/**
 * 场景类。
 * @class
 * @extends w.Group
 * @memberof w
 */
class Scene extends Group {
  /**
   * 创建场景
   * @constructor
   * @param {w.Engine} 【必选】opts.engine 场景所使用的engine对象。
   * @param {Array<String>} 【可选】opts.background 场景所使用的背景，一般为六张图片。
   * @example
   * let background=[
   *     `../demo/pub/assets/skybox/blueSky/pos-x.jpg`,
   *     `../demo/pub/assets/skybox/blueSky/neg-x.jpg`,
   *     `../demo/pub/assets/skybox/blueSky/pos-y.jpg`,
   *     `../demo/pub/assets/skybox/blueSky/neg-y.jpg`,
   *     `../demo/pub/assets/skybox/blueSky/pos-z.jpg`,
   *     `../demo/pub/assets/skybox/blueSky/neg-z.jpg`
   *   ];
   * @param {Array<String>} 【可选】opts.environment 场景所使用的IBL光，一般为六张图片。
   * let background=[
   *     `../demo/pub/assets/skybox/blueSky/pos-x.jpg`,
   *     `../demo/pub/assets/skybox/blueSky/neg-x.jpg`,
   *     `../demo/pub/assets/skybox/blueSky/pos-y.jpg`,
   *     `../demo/pub/assets/skybox/blueSky/neg-y.jpg`,
   *     `../demo/pub/assets/skybox/blueSky/pos-z.jpg`,
   *     `../demo/pub/assets/skybox/blueSky/neg-z.jpg`
   *   ];
   */
  constructor(opts = {}) {
    super(opts);
    this._type = "Scene";
    this.engine = must(opts.engine);
    this.event = new Event(EventType.SceneChange);
    this._views = []; //视图
    this._popups = [];
    this._cameraIndex = 0;
    this._defaultView=undefined;
    this.background = opts.background;
    this.environment = opts.environment;
    this.commands = new Map();
    this.initCommand();
  }
  set defaultView(v){
    this._defaultView=v;
    return this;
  }
  get defaultView(){
    return this._defaultView;
  }
  initCommand() {
    this.commands.set("setVisibility", [
      { entity: null, component: null, sub: null, param: { visibility: null } },
    ]);
    this.commands.set("playAnimation", [
      {
        entity: null,
        component: "Animation",
        sub: null,
        param: { action: "start", time: null, invert: false },
      },
    ]); //start stop resume
  }
  exec(commandStr, opts = {}) {
    let commands = this.commands.get(commandStr);
    if (!commands) {
      console.log("未找到执行指令(" + commandStr + param + ")");
      return;
    }
    for (let i = 0; i < commands.length; i++) {
      let command = commands[i];
      let eid = any(opts.entity, command.entity);
      let entity = this.engine.register.entityManager.get(eid);
      if (!entity) {
        console.log("未找到指令执行实体(" + commandStr + opts + ")");
        continue;
      }
      let target = entity;
      if (command.component) {
        //执行组件上的指令
        target = entity.getComponent(ComponentType[command.component]);
        if (target) {
          if (command.sub) {
            target = target[command.sub];
          }
        }
        if (target) {
          if (command.component == "Animation") {
            //动画特殊处理
            let action = opts.param["action"] || command.param["action"];
            let time = opts.param["time"] || command.param["time"];
            let invert = opts.param["invert"] || command.param["invert"];
            if (action == "start") {
              target.start(time, invert);
            } else if (action == "stop") {
              target.stop();
            } else {
              target.resume(invert);
            }
          } else {
            for (let key in command.param) {
              let v = opts.param[key] || command.param[key];
              target[key] = v;
            }
          }
        } else {
          console.log("指令执行目标不存在(" + commandStr + opts + ")");
        }
      } else {
        for (let key in command.param) {
          //执行实体上的指令
          let v = opts.param[key] || command.param[key];
          if (key === "visibility") {
            entity.setVisibility(v);
          } else {
            entity[key] = v;
          }
        }
      }
    }
  }
  /**
   *
   * @param {String} name 命令名称
   * @param {Array} man 命令体[{entity,componentType,param}]
   */
  addCommand(name, man) {
    this.commands.set(name, man);
  }
  removeCommand(name) {
    this.commands.delete(name);
  }
  get popups() {
    return this._popups;
  }
  get views() {
    return this._views;
  }
  get background() {
    return this._background;
  }
  set background(v) {
    if (!v) return;
    if (this.skybox) this.skybox.visibility = false;
    let skyboxMaterial = new SkyboxMaterial({ map: v });
    let skyboxGeometry = new SkyboxGeometry();
    let skyBoxMesh = new Mesh({
      material: skyboxMaterial,
      geometry: skyboxGeometry,
    });
    this.skybox = new Object3D({ rotation: [0, 0, 0] });
    this.skybox.addComponent(skyBoxMesh);
    this.addEntity(this.skybox);
  }
  get environment() {
    return this._environment;
  }
  set environment(v) {
    if (!v) return;
    let enviromentLight = Register.instance
      .manager(ComponentType.EnviromentLight)
      .get("x");
    if (!enviromentLight) {
      enviromentLight = new EnviromentLight({ id: "x", value: v });
      this.addComponent(enviromentLight);
    } else {
      enviromentLight.value = v;
    }
  }
  async init(opts = {}) {
    //注册系统
    this.addComponent(new MeshRender({ id: "x" })); //添加MeshRender组件。
    this.addComponent(new PunctualLight({ id: "x" })); // 添加PunctualLight组件。
    this.addComponent(new PostProcessing({ id: "x" })); //添加后处理组件。
    this.addComponent(new Pick({ id: "x" })); //添加拾取控件。
    this.addComponent(new Plot({ id: "x" })); //添加标绘控件。
    this.addComponent(new Transform({ id: "x" })); //添加变换控件
    //添加缺省相机
    let cameraEntity = new Object3D({
      id: "w3d-camera",
      position: [0, 0, 10],
      rotation: [0, 0, 0],
    });
    let camera = new Camera();
    cameraEntity.addComponent(camera);
    this.addEntity(cameraEntity);
    //添加缺省的控制器对象
    this.addComponent(new Orbit({ id: 'x', target: camera }));
    //添加缺省View
    //添加环境光
    let ambientLight = new AmbientLight({
      id: "x",
      intensity: opts.ambientIntensity,
      color: opts.ambientColor,
    });
    this.addComponent(ambientLight);
    //添加半球光
    let hemisLight = new HemisLight({ id: "x" });
    this.addComponent(hemisLight);
    //添加缺省环境光
    // let views=new Views();
    // this.addComponent(views);
    //添加背景组件
    let background = new Background();
    this.addComponent(background);
    //激活0号相机
    this.activeCamera(0);
  }
  /**
   * 激活相机对象。
   * @method
   * @param {Number} index 相机索引。
   * @return {Camera} 返回索引为index的相机对象。
   */
  activeCamera(index = 0) {
    let cameras = [
      ...Register.instance.manager(ComponentType.Camera).components.values(),
    ];
    let length = cameras.length;
    this._cameraIndex = Math.min(index, length - 1);
    for (let i = 0; i < length; i++) {
      if (i === index) {
        cameras[i].active();
      } else {
        cameras[i].deactive();
      }
    }
    return cameras[index];
  }
  /**
   * 获取当前激活的相机。
   * @return {Camera}
   */
  getCamera() {
    let cameras = [
      ...Register.instance.manager(ComponentType.Camera).components.values(),
    ];
    return cameras[this._cameraIndex];
  }
  /**
   * 根据id获取弹窗。
   * @method
   * @param {Stirng} id
   * @return {w.info.Popup} 返回id匹配上的弹窗。
   */
  getPopup(id) {
    return this.popups.find((popup) => popup.id === id);
  }
  /**
   * 移除弹窗。
   * @method
   * @param {w.info.Popup|String} Popup对象或Popup对象的id
   * @return {Scene} 返回移除后场景中所有的弹窗。
   */
  removePopup(popup) {
    let id, pop;
    if (popup instanceof Popup) {
      id = popup.id;
      popup.remove();
    } else {
      id = popup;
      pop = this.getPopup(id);
      if (pop) {
        pop.remove();
      }
    }
    let index = this.popups.findIndex((popup) => popup.id === id);
    if (index > -1) {
      this.popups.splice(index, 1);
    }
    return this.popups;
  }
  /**
   * 添加弹窗。
   * @method
   * @param {w.info.Popup} popup
   * @return {Scene} 返回场景本身。
   */
  addPopup(popup) {
    if (!this.getPopup(popup.id)) {
      popup.scene = this;
      popup.show();
      this.popups.push(popup);
    } else {
      let index = this.popups.findIndex((item) => item.id === popup.id);
      if (index > -1) {
        this.popups.splice(index, 1);
      }
      popup.scene = this;
      popup.show();
      this.popups.push(popup);
    }
    return this;
  }
  /**
   * 移除所有弹窗。
   * @method
   * @return {Scene} 返回场景本身。
   */
  removeAllPopups() {
    this.popups.forEach((popup) => {
      popup.remove();
    });
    this.popups.clear();
    return this;
  }

  /**
   * 场景销毁。
   * @method
   */
  destroy(canvasDistroy) {
    this.engine.register.systemManager.get("rendersystem").once = true;
    Register.instance.manager(ComponentType.MeshRender).get("x").clear();
    this.engine.register.systemManager.get("rendersystem").deactive();
    this.clearEvent();
    //this.views = null;
    //遍历所有的group,执行销毁
    for (let i = 0; i < this.children.length; i++) {
      this.children[i].destroy(false);
    }
    if (this.popups.length) {
      this.removeAllPopups();
    }
    //销毁所有实体
    this.removeAllEntity();
    //销毁所有组件
    this.removeAllComponent();
    //移除canvas
    if (canvasDistroy) {
      this.engine.canvas.parentElement.removeChild(engine.canvas);
      this.engine.canvas = null;
    }
  }
  update() {
    this.engine.register.systemManager.get("rendersystem").frame();
    this.publish(this.event);
  }
  /**
   * 添加动画
   * @method
   * @param {Animation} v 动画对象
   */
  addAnimation(v) {
    this.engine.register.manager(ComponentType.Animation).add(v);
    this.update();
  }
  /**
   * 移除组件
   * @method
   * @param {String} id 组件实例ID。
   * @return {Animation} 返回动画组件。
   */
  removeAnimation(id) {
    this.engine.register.manager(ComponentType.Animation).remove(id);
    this.update();
  }
  /**
   * 查询动画组件
   * @method
   * @param {String} id 组件实例ID。
   * @return {Animation} 返回动画组件。
   */
  getAnimation(id) {
    return this.engine.register.manager(ComponentType.Animation).get(id);
  }
  /**
   * 是否存在动画组件
   * @method
   * @param {String} id 组件实例ID。
   * @return {Boolean} 返回动画组件是否存在。
   */
  hasAnimation(id) {
    this.engine.register.manager(ComponentType.Animation).has(id);
  }
  async exportFrameBuffer() {
    // let base64= this.engine.canvas.toDataURL('image/png');
    // console.log(base64);
    // return base64;
    const commandEncoder = this.engine.device.createCommandEncoder();
    // 设置拷贝操作的源和目标
    const size = this.engine.size;
    const bytesPerRow = Math.ceil((size[0] * 4) / 256) * 256;
    const height = size[1];
    const buffer = this.engine.device.createBuffer({
      size: bytesPerRow * height,
      usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
    });
    const colorView = {
      texture: Pass.instance().swapAttach[1],
    };
    const bufferView = {
      buffer,
      bytesPerRow: bytesPerRow, // 每行的字节数
      rowsPerImage: height, // 每张图片的行数
    };
    // 将纹理数据拷贝到缓冲区
    commandEncoder.copyTextureToBuffer(colorView, bufferView, size);
    const commandBuffer = commandEncoder.finish();
    await this.engine.device.queue.submit([commandBuffer]);
    // 使用 mapReadAsync 从缓冲区中读取像素数据
    await buffer.mapAsync(GPUMapMode.READ);
    // 获取像素数据
    let position = new Uint8Array(buffer.getMappedRange());
    let data = position.slice(0, position.length);
    position = null;
    buffer.unmap();
    console.log(data, 111, size);
    return { size, data };
  }
  async screenshot() {
    await this.engine.register.systemManager.get("rendersystem").render();
    let base64 = this.engine.canvas.toDataURL("image/png");
    return base64;
  }
  getExtent(expand = 1.5) {
    let camera = this.getCamera();
    let center = camera.at;
    let offy = camera.distance * Math.tan(0.5 * camera.fovy);
    let offx = offy * camera.aspect;
    return [
      center.x - offx * expand,
      center.y - offy * expand,
      center.x + offx * expand,
      center.y + offy * expand,
    ];
  }
}
export { Scene };
